#include <algorithm>
#include "Triangle.h"

#define EPSILON 0.0000000005
//#define TRIANGLE_INTERSECTION_1

Triangle::Triangle( Vector4D v0, Vector4D v1, Vector4D v2, Vector4D tex0, Vector4D tex1, Vector4D tex2 )
{
    _v0 = v0;
    _v1 = v1;
    _v2 = v2;
    
    _tex0 = tex0;
    _tex1 = tex1;
    _tex2 = tex2;
}
    


Triangle::~Triangle()
{
    
}


#ifdef TRIANGLE_INTERSECTION_1
bool Triangle::computeRayIntersection( Ray& ray, double& t )
{
    Vector4D u = _v1 - _v0;
    Vector4D v = _v2 - _v0;
    Vector4D normal = cross( u, v );
    normal.normalize();
    
    // calcula interseção com o plano do triangulo
    t = dot( (_v0 - ray.origin), normal ) / dot( ray.direction, normal ) ;    
    Vector4D point = ray.origin + t*ray.direction;
        
    // testa se está dentro do triangulo
    double dotValue = dot( normal, cross(u, (point-_v0)) );
    
    if (dotValue < 0.0)
        return false;
    
    dotValue = dot( normal, cross( (_v2-_v1), (point-_v1)) );
    
    if (dotValue < 0.0)
        return false;
    
    dotValue = dot( normal, cross( (_v0-_v2), (point-_v2)) );
    
    if (dotValue < 0.0)
        return false;
    
    return true;  
}



#else
bool Triangle::computeRayIntersection( Ray& ray, double& t )
{
    Vector4D edge1 = _v1 - _v0;
    Vector4D edge2 = _v2 - _v0;
    
    Vector4D pVec = cross( ray.direction, edge2 );
    double det = dot( edge1, pVec );
    
    if (det > -EPSILON && det < EPSILON)
        return false;
    
    double inv_det = 1.0/det;
    
    // calculate distance from vert0 to ray origin
    Vector4D tVec = ray.origin - _v0;
    
    // calculate U parameter and test bounds
    double u = dot( tVec, pVec ) * inv_det;
    if (u < 0.0 || u > 1.0)
        return false;
    
    // prepare to test v parameter
    Vector4D qVec = cross( tVec, edge1 );
    
    // calculate v parameter and test bounds
    double v = dot( ray.direction, qVec ) * inv_det;
    if (v < 0.0 || (u + v) > 1.0)
        return false;
    
    // calculate t, ray intersects triangle
    t = dot( edge2, qVec ) * inv_det;    

    return true;
}
#endif



Vector4D Triangle::getNormal( Vector4D& point )
{
    Vector4D normal = cross( (_v1 -_v0), (_v2 -_v0) );
    normal.normalize();
    
    return normal;
}



void Triangle::setMaterial( int materialId )
{
    _materialId = materialId;
}



int Triangle::getMaterialId()
{
    return _materialId;
}



Object::ObjectType Triangle::getType()
{
    return Object::TRIANGLE;
}



void Triangle::getBoundingBox( Vector4D& min, Vector4D& max )
{
    min.x = std::min( _v0.x, std::min( _v1.x, _v2.x ) );
    min.y = std::min( _v0.y, std::min( _v1.y, _v2.y ) );
    min.z = std::min( _v0.z, std::min( _v1.z, _v2.z ) );
    
    max.x = std::max( _v0.x, std::max( _v1.x, _v2.x ) );
    max.y = std::max( _v0.y, std::max( _v1.y, _v2.y ) );
    max.z = std::max( _v0.z, std::max( _v1.z, _v2.z ) );
}



void Triangle::getVertices( Vector4D& v0, Vector4D& v1, Vector4D& v2 )
{
    v0 = _v0;
    v1 = _v1;
    v2 = _v2;
}



void Triangle::getTextureCoordinates( Vector4D& tex0, Vector4D& tex1, Vector4D& tex2 )
{
    tex0 = _tex0;
    tex1 = _tex1;
    tex2 = _tex2;
}
